#ifndef BOSS_Afterburner_AxisBinning_H
#define BOSS_Afterburner_AxisBinning_H

#include "TString.h"
#include <utility> // for std::pair
#include <yaml-cpp/yaml.h>

/// @addtogroup Afterburner_objects
/// @{

/// Container object for the binning for *one* axis.
/// Only contains the number of bins, the bin width and the plot range (from to).
/// @author   Remco de Boer 雷穆克 (r.e.deboer@students.uu.nl or remco.de.boer@ihep.ac.cn)
/// @date     March 23rd, 2019
class AxisBinning
{
public:
  AxisBinning() : fHasBinning(false), fNBins(0), fBinWidth(0.) {}
  AxisBinning(const TString& input);
  AxisBinning(const Int_t& nbins, const Double_t& from, const Double_t& to);
  AxisBinning(const Double_t& width, const Double_t& from, const Double_t& to);

  void SetBinning(const YAML::Node& input);
  void SetBinning(const TString& input);
  void SetBinning(const Int_t& nbins, const Double_t& from, const Double_t& to);
  void SetBinning(const Double_t& width, const Double_t& from, const Double_t& to);

  void SetBinWidth(const Double_t& width);
  void SetNBins(const Int_t& nbins);
  void SetRange(const Double_t& from, const Double_t& to);
  void SetFrom(const Double_t& from) { SetRange(from, fRange.second); }
  void SetTo(const Double_t& to) { SetRange(fRange.first, to); }

  const Bool_t    HasBinning() const { return fHasBinning; }
  const Int_t&    NBins() const;
  const Double_t& From() const;
  const Double_t& To() const;
  const Double_t& BinWidth() const;
  const Double_t  ComputeRange() const { return ComputeRange(fRange.first, fRange.second); }

  virtual const TString BuildString() const;
  const TString         BuildString_AxisBinning() const;

private:
  Bool_t   fHasBinning;
  Int_t    fNBins;
  Double_t fBinWidth;

  std::pair<Double_t, Double_t> fRange;

  static constexpr Int_t gMaxNBins = 1e4;

  void SetBinning(const Int_t& nbins, const Double_t& width, const Double_t& from,
                  const Double_t& to);

  const Bool_t CheckNBins(const Int_t& nbins) const;
  const Bool_t CheckBinWidth(const Double_t& width, const Double_t& from, const Double_t& to) const;
  const Bool_t CheckRange(const Double_t& from, const Double_t& to) const;

  const Double_t ComputeRange(const Double_t& from, const Double_t& to) const;
  const Double_t ComputeBinWidth(const Int_t& nbins, const Double_t& from,
                                 const Double_t& to) const;
  const Int_t ComputeNBins(const Double_t& width, const Double_t& from, const Double_t& to) const;
};

/// @}
#endif